package middleware

import (
	"errors"
	"time"

	"gitee.com/krio/ginc/example/core/exerr"
	"gitee.com/krio/ginc/example/core/lib/conf"
	"gitee.com/krio/ginc/example/router/output"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

// 参数名称
const (
	MiddlewareKeyToken = "Token"  // 中间件标识
	tokenParam         = "token"  // header字段名称
	claimsParam        = "claims" // 荷载参数字段名称
)

// JWTAuth token鉴权
func JWTAuth(ctx *gin.Context) {
	token := ctx.Request.Header.Get(tokenParam)
	// 初始化一个JWT对象实例
	newJWT := NewJWT()
	// 解析token中包含的相关信息(有效载荷)
	claims, err := newJWT.ParseToken(token)
	if err != nil {
		output.Response(ctx, "", exerr.Parse(err))
		ctx.Abort()
		return
	}
	// 继续交由下一个路由处理,并将解析出的信息传递下去
	ctx.Set(claimsParam, claims)

	ctx.Next()
}

// JWT 签名结构
type JWT struct {
	SigningKey []byte
}

// NewJWT 新建一个jwt实例
func NewJWT() *JWT {
	return &JWT{
		[]byte(GetSignKey()),
	}
}

// CreateToken 生成令牌
func CreateToken(ctx *gin.Context, userId string) (*CustomClaims, string, error) {
	j := NewJWT()
	// 构造用户claims信息(负荷)
	expiresAt := time.Now().Add(time.Duration(conf.C.Login.ExpiresTime) * time.Minute).Unix()
	claims := CustomClaims{
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expiresAt,           // 签名过期时间(分钟)
			Issuer:    conf.C.Login.Issuer, // 签名颁发者
			Id:        userId,              // 用户ID
		},
	}
	token, err := j.CreateToken(claims)
	if err != nil {
		return nil, "", err
	}

	return &claims, token, nil
}

// CustomClaims 载荷，可以加一些自己需要的信息
type CustomClaims struct {
	jwt.StandardClaims
}

// GetSignKey 获取signKey
func GetSignKey() string {
	return conf.C.Login.SignKey
}

// SetSignKey 这是SignKey
// func SetSignKey(key string) string {
// 	signKey = key
// 	return signKey
// }

// CreateToken 生成一个token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(j.SigningKey)
}

// ParseToken 解析Token
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	if tokenString == "" {
		return nil, exerr.TokenEmpty
	}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		var ve *jwt.ValidationError
		if errors.As(err, &ve) {
			switch ve.Errors {
			// 无效
			case jwt.ValidationErrorMalformed:
				return nil, exerr.TokenInvalid
			// 过期
			case jwt.ValidationErrorExpired:
				return nil, exerr.TokenExpired
			// 尚未生效
			case jwt.ValidationErrorNotValidYet:
				return nil, exerr.TokenNotValidYet
			// 其他
			default:
				return nil, exerr.TokenInvalid
			}
		}
	}
	// 将token中的claims信息解析出来并断言成用户自定义的有效载荷结构
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, exerr.TokenInvalid
}

// GetClaims get claims
func GetClaims(ctx *gin.Context) *CustomClaims {
	// 获取Payload信息
	return ctx.MustGet(tokenParam).(*CustomClaims)
}

// RefreshToken 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})
	if err != nil {
		return "", err
	}
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(time.Duration(conf.C.Login.ExpiresTime) * time.Minute).Unix()
		return j.CreateToken(*claims)
	}
	return "", exerr.TokenInvalid
}
